using System;
using Sharp3D.Math.Core;
using ReAnimation.Helpers.Common;
using Sharp3D.Math.Geometry3D;
using GLWrap.MathExt;
using System.Diagnostics;

namespace GLWrap
{
    [Serializable]
    public class Camera : ICamera
    {
        private Vector3F _eye;
        private Vector3F _view;
        private Vector3F _up;

        public Vector3F Eye 
        {
            get
            {
                return new Vector3F(_eye);
            }
            set
            {
                _eye = value;
                CameraPositionChanged.Throw(this, EventArgs.Empty);
            }
        }

        public Vector3F View 
        {
            get
            {
                return new Vector3F(_view);
            }
            set
            {
                _view = value;
                CameraPositionChanged.Throw(this, EventArgs.Empty);
            }
        }

        public Vector3F Up 
        {
            get
            {
                return new Vector3F(_up);
            }
            set
            {
                _up = value;
                CameraPositionChanged.Throw(this, EventArgs.Empty);
            }
        }
        [field: NonSerialized]
        public event EventHandler CameraPositionChanged;

        public Camera()
        {
            Eye = new Vector3F(0, 0, 0);
            View = new Vector3F(0, 0, -1);
            Up = new Vector3F(0, 1, 0);
        }

        public Camera(Vector3F eye, Vector3F view, Vector3F up)
        {
            Eye = eye;
            View = view;
            Up = up;
        }

        public void Rotate(float angle, Vector3F vector)
        {
           // Debug.WriteLine("Angle = {0}, vector = {1},{2},{3}".F(angle, vector.X, vector.Y, vector.Z));
            Matrix4F translateMatrix = Matrix4FExtension.TranslateMatrix(Eye);
            Matrix4F translateMatrix2 = Matrix4FExtension.TranslateMatrix(-Eye);
            Matrix4F rotationMatrix = Matrix4FExtension.RotationMatrix(angle, vector);

            Matrix4F result = translateMatrix*rotationMatrix * translateMatrix2;

            View = result.Multiply(View);
            //View.Normalize();
        }

        public void Move(Direction direction, float coefficient)
        {
            Vector3F lookVector;
            Vector3F displacementVector;
            lookVector = View - Eye;
            lookVector.Normalize();
            displacementVector = Vector3F.CrossProduct(Up, lookVector);
            displacementVector.Normalize();

            lookVector *= coefficient;
            displacementVector *= coefficient;

            switch (direction)
            {
                case Direction.Up:               
                    View += lookVector;
                    Eye += lookVector;
                    break;
                case Direction.Down:
                    View -= lookVector;
                    Eye -= lookVector;
                    break;
                case Direction.Left:
                    View += displacementVector;
                    Eye += displacementVector;
                    break;
                case Direction.Right:
                    View -= displacementVector;
                    Eye -= displacementVector;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("direction");
            }
        }

        public object Clone()
        {
            return new Camera()
            {
                View = this.View,
                Up = this.Up,
                Eye = this.Eye
            };
        }
    }
}